#include "Mesh.h"
#include "../stdafx.h"

Mesh::Mesh() : 
	vertices(0),
	numVertices(0)
{
}

Mesh::Mesh(Vector3f* vertices, int count) {
	this->vertices = vertices;
	this->numVertices = count;
}

Mesh::~Mesh()
{
}

/* at the moment, only triangle list mode is supported */
void Mesh::Load(Vector3f* input, int count) {
	int i;
	if (vertices)
		ReleaseNewArray(vertices);
	
	vertices = new Vector3f[count];
	for (i = 0; i < count; i++)
		vertices[i] = input[i]; //copy
	numVertices = count;
	
	/* position */
	pos = vertices[0];
}



int Mesh::TestHit(Ray3f ray, HitInfo* hit, int& count) {
	int i;
	/* iterate all triangles */
	Vector3f* p = vertices;
	int firstHit = 0;
	HitInfo minHit;
	for (i = 0; i < numVertices; i += 3, p += 3) {
		TestHitTriangle(p, ray, hit, count);
		
		/* find the closest */
		if (count > 0) {
			if (firstHit == 0) {
				minHit = hit[0];
				
				firstHit = 1;						
			} else if (minHit.point.z < hit[0].point.z) {
				minHit = hit[0]; //save hit info
			}
		}
	}
	
	if (firstHit) {//copy back
		hit[0] = minHit;
		count = 1;
	} else count = 0;
	return count;
}

float Determinant(float mat[9]) {
	float a = mat[0];
	float b = mat[1];
	float c = mat[2];
	float d = mat[3];
	float e = mat[4];
	float f = mat[5];
	float g = mat[6];
	float h = mat[7];
	float i = mat[8];
	
	return a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g;
}
int Mesh::TestHitTriangle(Vector3f* tri, Ray3f ray, HitInfo* hit, int& count) {
	count = 0;
	
	float xa = tri[0].x;
	float ya = tri[0].y;
	float za = tri[0].z;
	float xb = tri[1].x;
	float yb = tri[1].y;
	float zb = tri[1].z;
	float xc = tri[2].x;
	float yc = tri[2].y;
	float zc = tri[2].z;
	float xd = ray.direction.x;
	float yd = ray.direction.y;
	float zd = ray.direction.z;
	float xe = ray.point.x;
	float ye = ray.point.y;
	float ze = ray.point.z;
	
	float xab = xa - xb;
	float xac = xa - xc;
	float xae = xa - xe;
	
	float yab = ya - yb;
	float yac = ya - yc;
	float yae = ya - ye;
	
	float zab = za - zb;
	float zac = za - zc;
	float zae = za - ze;
	
	float matDenominator[] = {
		xab, xac, xd,
		yab, yac, yd,
		zab, zac, zd
	};

	float denominator = Determinant(matDenominator);
	if (denominator != 0) {		
		float matBeta[] = {
			xae, xac, xd,
			yae, yac, yd,
			zae, zac, zd
		};
		float beta = Determinant(matBeta) / denominator;
		if (0 <= beta && beta <= 1) {		
			float matGamma[] = {
				xab, xae, xd,
				yab, yae, yd,
				zab, zae, zd
			};
			float gamma = Determinant(matGamma) / denominator;
			if (0 <= gamma && gamma + beta <= 1) {			
				float matT[] = {
					xab, xac, xae,
					yab, yac, yae,
					zab, zac, zae
				};
				float t = Determinant(matT) / denominator;
				
				if (t >= EPSILON_HIT) {			
					/* triangle normal */
					Vector3f ab = tri[1] - tri[0];
					Vector3f ac = tri[2] - tri[0];
					Vector3f normal = ab.Cross(ac);
					if (normal.Dot(ray.direction) > 0)
						normal = -normal;
					
					/* hit */
					hit[count].point = ray.point + ray.direction*t;
					hit[count].normal = normal.Normalize();
					count++;
				}
			}
		}
	}
	return count;
}
